//
// Created by renyi on 16/6/14.
//

#include "SSL_Thread_Init.h"
#include "Common.h"
#include "Client.h"
#include "Json_parse.h"
#include "Sql_parse.h"


int jsonsend(SSL *ssl, char* buf){
    int len = 0;
    len = strlen(buf);
    len = htonl(len);
    len = SSL_write(ssl, &len, sizeof(len));

    if (len <= 0) {
        if (DEBUG){
            printf("消息size: '%lu'发送失败！错误代码是%d，错误信息是'%s'\n",strlen(buf), errno, strerror(errno));
        }
        //goto finish;
        return -1;
    } else{
        //printf("消息size: '%d'发送成功，共发送了%d个字节！\n",strlen(buf), len);
    }


    len = SSL_write(ssl, buf, strlen(buf));
    if (len <= 0) {
        //printf("消息'%s'发送失败！错误代码是%d，错误信息是'%s'\n\n",buf, errno, strerror(errno));
        return -1;
    } else{
        //printf("消息'%s'发送成功，发送了%d字节！\n\n",buf, len);
    }

    return 1;

}

int jsonrecv(SSL *ssl, char* buf){

    int size = 0, len = 0;
    SSL_read(ssl, &size, sizeof(size));
    size = ntohl(size);
    if (size > 0x100000){
        printf("size error\n");
        return -1;
    }

    bzero(buf, MAXBUF + 1);
    len = SSL_read(ssl, buf, size);
    if (len <= 0) {
        printf("消息接受失败！错误代码是%d，错误信息是'%s'\n", errno, strerror(errno));
        return -1;
    }
    printf("接收成功:%s，共%d字节\n\n",buf, len);
    return 1;
}



static void* thread_main(void *arg)
{
    int s, err, len;
    SSL        *ssl, *xl_ssl;
    MYSQL *mysql;
    unsigned long thread_id = SSL_pthreads_thread_id();

    char buf[MAXBUF + 1] ,
            device_id[64],
            device_sn[20],
            device_name[20],
            query[1000],
            password[10];

    ssl=(SSL *)arg;
    s=SSL_get_fd(ssl);
    err = SSL_accept (ssl);
    if(err<0)
    {
        printf("ssl SSL_accept err\n");
        return NULL;
    }

    struct sockaddr_in addr;
    socklen_t addr_len = sizeof(addr);
    getpeername( s, (struct sockaddr *)&addr, &addr_len );

    printf (
            "SSL connection using %s threadid:%lu  ip:%s port %d\n",
            SSL_get_cipher (ssl),
            thread_id,
            inet_ntoa(addr.sin_addr),
            ntohs(addr.sin_port)
    );

    //进行信息验证
    /* X509 *client_cert;
     client_cert = SSL_get_peer_certificate(ssl);
     if (client_cert != NULL) {
         printf ("Client certificate:\n");

         //读取证书subject名并显示
         char *str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
         if (NULL == str) {
             printf("认证出错!\n");
         }
         X509_free (client_cert);*//*如不再需要,需将证书释放 *//*
        OPENSSL_free(str);
    }
    else {
        printf("找不到客户端的认证证书\n");
        sleep(5000);
        goto finish;//接受失败
    }*/

    //初始远程客服端SSL
    xl_ssl = init_ssl_client();
    if (xl_ssl == NULL){
        return NULL;
    }

    //初始化mysql连接
    mysql = SQL_setup();
    if (mysql == NULL){
        return NULL;
    }

    for(;;){

        //读取XL服务器
        bzero(buf, MAXBUF + 1);
        len = jsonrecv(xl_ssl, buf);
        if (len <= 0 ){
            goto finish;//接受失败
        }

        //解析密码
        password_parse(buf,password);

        if (strlen(device_sn) > 10 && strlen(password) > 5){
            //更新数据库更新密码
            if(DEBUG){
                sprintf(query, "UPDATE kj SET password='%s', ip='%s' WHERE  sn=%s", password, inet_ntoa(addr.sin_addr), device_sn);
                update(mysql, query);
                printf("device_sn:%s----password:%s\n", device_sn, password);
            }
        }

        //发送到KJ
        len = jsonsend(ssl, buf);
        if (len <= 0 ){
            goto finish;//发送失败
        }

        //读取KJ
        bzero(buf, MAXBUF + 1);
        len = jsonrecv(ssl, buf);
        if (len <= 0 ){
            goto finish;//接受失败
        }

        //验证SN 不对不代理
        len = device_parse(buf, device_id, device_name, device_sn);
        if (len < 0){
            printf("threadid:%lu---json解析失败", thread_id);
            goto finish;
        }
        if (len == 2){
            //记录数据
            sprintf(query, "UPDATE kj SET info='%s' WHERE  sn=%s", buf, device_sn);
            update(mysql, query);
        }

        //printf("sn:%s  %d", device_sn, strlen(device_sn));

        if (strlen(device_sn) > 10){
            int has_device = getInfo(mysql, device_sn, buf);
            if (has_device){
                printf("threadid:%lu---数据库验证通过\n\n", thread_id);
            }else{
                printf("threadid:%lu---数据库验证失败\n\n", thread_id);
                goto finish;
            }
        }

        //发送到XL
        len = jsonsend(xl_ssl, buf);
        if (len <= 0 ){
            goto finish;//发送失败
        }

        sleep(1);
    }

    finish:

    bzero(buf, MAXBUF + 1);
    bzero(device_id, strlen(device_id));
    bzero(device_sn, strlen(device_sn));
    bzero(device_name, strlen(device_name));
    bzero(query, strlen(query));
    bzero(password, strlen(password));

    mysql_close(mysql);
    SSL_free (xl_ssl);
    SSL_free (ssl);
    close(s);
    return((void *)0);
}

int main(int argc, char **argv){
    fflush(stdout);
    setvbuf(stdout,NULL,_IONBF,0);

    int i, err, sockfd, new_fd;
    SSL_CTX *ctx;
    SSL *ssl;
    struct sockaddr_in my_addr, their_addr;
    pthread_t pid;
    socklen_t len;

    init_OpenSSL();
    ctx = SSL_CTX_new (SSLv23_server_method());

    if (ctx == NULL) {
        ERR_print_errors_fp(stderr);
        exit(1);
    }
    
    //设置会话的握手方式并加载CA证书
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_load_verify_locations(ctx, XL_CA, NULL);
    

    if (SSL_CTX_use_certificate_file(ctx, XLCA, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(1);
    }
    if (SSL_CTX_use_PrivateKey_file(ctx, XLCAKEY, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(1);
    }
    if (!SSL_CTX_check_private_key(ctx)) {
        ERR_print_errors_fp(stderr);
        exit(1);
    }

    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    } else{
        printf("socket created\n");
    }

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;

    if (argv[2])
        my_addr.sin_port = htons(atoi(argv[2]));
    else
        my_addr.sin_port = htons(PORT);

    if (argv[1])
        my_addr.sin_addr.s_addr = inet_addr(argv[1]);
    else
        my_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))== -1) {
        perror("bind");
        exit(1);
    } else{
        printf("%s:%d\n", argv[1], atoi(argv[2]));
        printf("binded\n");
    }

    if (listen(sockfd, LSNUM) == -1) {
        perror("listen");
        exit(1);
    } else
        printf("begin listen\n");

    SSL_thread_setup();

    while(1)
    {
        struct timeval tv;
        fd_set fdset;
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        FD_ZERO(&fdset);
        FD_SET(sockfd, &fdset);
        select(sockfd+1, &fdset, NULL, NULL, (struct timeval *)&tv);
        if(FD_ISSET(sockfd, &fdset))
        {
            new_fd=accept(sockfd, (struct sockaddr *) &their_addr, &len);
            ssl = SSL_new (ctx);
            CHK_NULL(ssl);
            err=SSL_set_fd(ssl, new_fd);
            if(err>0)
            {
                err=pthread_create(&pid,NULL,&thread_main,(void *)ssl);
                pthread_detach(pid);
            }
            else{
                continue;
            }
        }

    }

    SSL_shutdown(ssl);
    SSL_free(ssl);
    close(new_fd);

    SSL_thread_cleanup();
    SSL_CTX_free (ctx);
    return 0;
}
